﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;

//added - patryk
using System.IO;
using System.Xml.XPath;
using System.Globalization;

namespace ParameterManagementSystem
{
    public enum DisplayMethod : byte { RANGE, OCCURENCES };

    public partial class CompareUserControl : UserControl
    {
        private Dictionary<string, XmlFile> xmlFilesListReference;
        private ListBox listBoxXmlFilesReference;


        private CompareViewRecord selectedItem;
        public CompareViewRecord SelectedItem
        {
            get { return selectedItem; }
            set
            {
                if (SelectedItem != null)
                {
                    SelectedItem.Unselect();
                }
                selectedItem = value;
                if (SelectedItem != null)
                {
                    SelectedItem.Select();
                }
            }
        }

        public CompareUserControl(ref Dictionary<string, XmlFile> xmlFilesList, ref ListBox listBoxXmlFiles)
        {
            InitializeComponent();
            xmlFilesListReference = xmlFilesList;
            listBoxXmlFilesReference = listBoxXmlFiles;
        }

        /// <summary>
        /// Here all the comparison takes place
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void compareButton_Click(object sender, EventArgs e)
        {
            //iterator.Current.SelectChildren(XPathNodeType.All).Count

            ///this is a dictionary holding entries with values of all parameters with associated filename and value(if any)
            Dictionary<parameterID, Dictionary<String, object>> parameterValuesDictionary = new Dictionary<parameterID, Dictionary<String, object>>();

            Dictionary<string, XPathDocument> XMLsToWorkWith = new Dictionary<string, XPathDocument>();

            var form = Form.ActiveForm as Form1;
            if (form != null)
            {
                listBoxXmlFilesReference = form.ListBoxXmlFiles;
                xmlFilesListReference = form.XmlFilesList;
            }

            #region loading of all/selected XMLs to the XPathDocList
            if (listBoxXmlFilesReference.SelectedIndices.Count < 2)// all elements
            {
                foreach (string fileName in xmlFilesListReference.Keys)
                {
                    if (listBoxXmlFilesReference.Items.Contains(fileName))
                    {
                        XmlFile l_xmlFile = new XmlFile();
                        if (xmlFilesListReference.TryGetValue(fileName, out l_xmlFile))
                        {
                            StringReader strReader = new StringReader(l_xmlFile.content);
                            XMLsToWorkWith.Add(fileName, new XPathDocument(strReader));
                        }
                    }
                }
            }
            else//selected elements
            {
                foreach (string fileName in listBoxXmlFilesReference.SelectedItems)
                {
                    if (listBoxXmlFilesReference.Items.Contains(fileName))
                    {
                        XmlFile l_xmlFile = new XmlFile();
                        if (xmlFilesListReference.TryGetValue(fileName, out l_xmlFile))
                        {
                            StringReader strReader = new StringReader(l_xmlFile.content);
                            XMLsToWorkWith.Add(fileName, new XPathDocument(strReader));
                        }
                    }
                }
            }
            #endregion

            #region Getting all the parameters with groups to the parameterValuesDictionary
            foreach (KeyValuePair<string, XPathDocument> DictionaryEntry in XMLsToWorkWith)
            {
                XPathNavigator thisNavigator = DictionaryEntry.Value.CreateNavigator();

                thisNavigator.MoveToRoot();
                thisNavigator.MoveToFirstChild();
                thisNavigator.MoveToFirstChild();

                do
                {
                    thisNavigator.MoveToAttribute("NAME", "");
                    string groupName = thisNavigator.Value;
                    thisNavigator.MoveToParent();
                    do
                    {
                        //----comparing param's attributes-----------------

                        thisNavigator.MoveToFirstChild();
                        thisNavigator.MoveToFirstAttribute();

                        do
                        {
                            switch (thisNavigator.Name)
                            {
                                case "NAME":
                                    break;
                                case "HINT":
                                    break;
                                case "TYPE":
                                    break;
                                case "VALUE": // this can be done better....... but anyways , let's continue :)
                                    //paramValue = Convert.ToDouble(thisNavigator.ValueAsDouble);
                                    object paramValue = null;
                                    double doubleValue;
                                    bool paramIsString = false;
                                    if (!Double.TryParse(thisNavigator.Value, NumberStyles.Number, CultureInfo.InvariantCulture/*CultureInfo.CreateSpecificCulture("en-GB")*/, out doubleValue))
                                    {
                                        paramValue = thisNavigator.Value;
                                        paramIsString = true;
                                    }

                                    //------- move to attribute doesn't work backwards (no idea why)
                                    // update: because it cannot go from attribute to attribute with the use of 
                                    //MoveToAttribute method (in this case only MoveToNextAttribute can be used)
                                    thisNavigator.MoveToParent();
                                    thisNavigator.MoveToFirstChild();
                                    thisNavigator.MoveToAttribute("NAME", "");

                                    //if no match with desired name 'break;' and go to the next one
                                    if (!thisNavigator.Value.Contains(this.parameterNameTextBox.Text))
                                    {
                                        thisNavigator.MoveToParent();
                                        thisNavigator.MoveToFirstChild();
                                        thisNavigator.MoveToAttribute("VALUE", "");
                                        break;
                                    }

                                    #region Adding parameter to parameterValuesDictionary
                                    //....add( (fileName, Concatenated String <groupName + , +  parameterName>), parameterValue)
                                    if (paramIsString)
                                    {
                                        // For this I could have written a function :)
                                        Dictionary<string, object> tempDictionary = new Dictionary<string, object>();

                                        ///if this parameter entry already exists, then only add it's difference entry in its dictionary
                                        if (parameterValuesDictionary.TryGetValue(new parameterID(thisNavigator.Value, groupName, true), out tempDictionary))
                                        //if (parameterValuesDictionary.ContainsKey(new parameterID(thisNavigator.Value, groupName)))
                                        {
                                            tempDictionary.Add(DictionaryEntry.Key, paramValue);
                                        }
                                        else
                                        {
                                            tempDictionary = new Dictionary<string, object>();
                                            tempDictionary.Add(DictionaryEntry.Key, paramValue);
                                            parameterValuesDictionary.Add(new parameterID(thisNavigator.Value, groupName, true), tempDictionary);
                                            //(DictionaryEntry.Key, groupName + ", " + thisNavigator.Value), paramValue);
                                        }
                                    }
                                    else
                                    {
                                        Dictionary<string, object> tempDictionary = new Dictionary<string, object>();

                                        ///if this parameter entry already exists, then only add it's difference entry in its dictionary
                                        if (parameterValuesDictionary.TryGetValue(new parameterID(thisNavigator.Value, groupName, false), out tempDictionary))
                                        //if (parameterValuesDictionary.ContainsKey(new parameterID(thisNavigator.Value, groupName)))
                                        {
                                            tempDictionary.Add(DictionaryEntry.Key, doubleValue);
                                        }
                                        else
                                        {
                                            tempDictionary = new Dictionary<string, object>();
                                            tempDictionary.Add(DictionaryEntry.Key, doubleValue);
                                            parameterValuesDictionary.Add(new parameterID(thisNavigator.Value, groupName, false), tempDictionary);
                                            //(DictionaryEntry.Key, groupName + ", " + thisNavigator.Value), paramValue);
                                        }
                                    }
                                    #endregion

                                    //again - doesn't work backwards
                                    thisNavigator.MoveToParent();
                                    thisNavigator.MoveToFirstChild();
                                    thisNavigator.MoveToAttribute("VALUE", "");

                                    break;
                                case "LEVEL":
                                    break;
                                default:
                                    break;
                            }


                        } while (thisNavigator.MoveToNextAttribute());
                        thisNavigator.MoveToParent();

                    } while (thisNavigator.MoveToNext());//iteration on params
                    thisNavigator.MoveToParent();

                } while (thisNavigator.MoveToNext());//iteration on groups

            }
            #endregion

            //useful debug help ^____^
            //printAllParamatersToFile("output.txt", parameterValuesDictionary);

            #region Do the house-keeping
            //foreach (Control item in this.contentsPanel.Controls)
            //{
            //    item.Dispose();
            //}
            this.contentsPanel.Controls.Clear();

            if (checkBoxShowOnlyDifferences.Checked)
            {
                for (int i = parameterValuesDictionary.Count - 1; i >= 0; i--)
                {
                    if (parameterValuesDictionary.ElementAt(i).Value.Values.Distinct().Count() == 1)
                    {
                        parameterValuesDictionary.Remove(parameterValuesDictionary.ElementAt(i).Key);
                    }
                }
            }

            //foreach (KeyValuePair<parameterID, Dictionary<String, object>>  item in parameterValuesDictionary)
            //{
            //    if (item.Value.Values.Distinct().Count() == 1)
            //    {
            //        parameterValuesDictionary.Remove(item.Key);
            //    }
            //} 
            #endregion

            //Add new records according to display type
            if (this.radioButtonShowRange.Checked)
            {
                updateControlWithParametersDictionary(parameterValuesDictionary, DisplayMethod.RANGE);
            }
            else
            {
                updateControlWithParametersDictionary(parameterValuesDictionary, DisplayMethod.OCCURENCES);
            }

        }

        /// <summary>
        /// Helpful function to list all the parameters to the <paramref name="fileName"/>
        /// </summary>
        /// <param name="fileName">Name of a file to which we save the parameters</param>
        /// <param name="parameterValuesDictionary">Dictionary with paramters</param>
        private void printAllParamatersToFile(String fileName, Dictionary<parameterID, Dictionary<String, object>> parameterValuesDictionary)
        {
            using (StreamWriter fileWriter = new StreamWriter(fileName))
            {
                foreach (KeyValuePair<parameterID, Dictionary<string, object>> entry in parameterValuesDictionary)
                {
                    fileWriter.WriteLine(System.String.Format("\nparameter {0} from group {1} have these values: ", entry.Key.Name, entry.Key.Group));
                    Dictionary<string, object> tempDict = new Dictionary<string, object>();
                    if (parameterValuesDictionary.TryGetValue(entry.Key, out tempDict))
                    {
                        foreach (KeyValuePair<string, object> difference in tempDict)
                        {
                            fileWriter.WriteLine("\t difference in file {0} , with value {1}", difference.Key, difference.Value);
                        }
                        fileWriter.WriteLine("\n");
                    }
                }
            }
        }
        private bool updateControlWithParametersDictionary(Dictionary<parameterID, Dictionary<String, object>> parameterValuesDictionary, DisplayMethod howToDisplay)
        {
            foreach (KeyValuePair<parameterID, Dictionary<String, object>> entry in parameterValuesDictionary)
            {
                this.contentsPanel.Controls.Add(new CompareViewRecord(entry, this, howToDisplay));
            }
            return true;
        }
    }
    public class parameterID : IEquatable<parameterID>
    {
        private String parameterName;
        private String parameterGroup;
        private bool parameterIsString;//when at least one of its values is String

        public String Group
        {
            get { return parameterGroup; }
            set { parameterGroup = value; }
        }
        public String Name
        {
            get { return parameterName; }
            set { parameterName = value; }
        }
        public bool IsAString
        {
            get { return parameterIsString; }
            set { parameterIsString = value; }
        }

        //these 3 methods allow to use TryGetValue and Contain methods on this class
        public bool Equals(parameterID other)
        {
            return other != null && parameterName == other.parameterName && parameterGroup == other.parameterGroup;
        }
        public override bool Equals(object other)
        {
            return Equals(other as parameterID);// base.Equals(other);
        }
        public override int GetHashCode()
        {
            int fHash = parameterName.GetHashCode();
            return ((fHash << 16) | (fHash >> 16)) ^ parameterGroup.GetHashCode();
            //return base.GetHashCode();
        }

        /// <summary>
        /// constructor that creates a parameterID with set parameterName and parameterGroup
        /// </summary>
        /// <param name="parameterName">Name of the parameter from the xml file.</param>
        /// <param name="parameterGroup">Group name in which the paramters resides.</param>
        public parameterID(String parameterName, String parameterGroup, bool isAString)
        {
            this.parameterName = parameterName;
            this.parameterGroup = parameterGroup;
            this.IsAString = isAString;
        }
    }
}
